package ast

import (
	"mi/compiler/token"
	"mi/internal/span"
	"mi/internal/xpath"
)

type TextRange interface {
	Pos() int
	End() int
	SetPos(pos int)
	SetEnd(end int)

	Set(key string, value interface{})
	Get(key string) interface{}
}

type textRange struct {
	pos int
	end int

	cache map[string] interface{}
}

func(t *textRange) Pos() int 		{ return t.pos }
func(t *textRange) End() int 		{ return t.end }
func(t *textRange) SetPos(pos int) 	{ t.pos = pos  }
func(t *textRange) SetEnd(end int) 	{ t.end = end  }
func(t *textRange) Set(key string, value interface{}) {
	if t.cache == nil {
		t.cache = make(map[string]interface{})
	}
	t.cache[key] = value
}
func(t *textRange) Get(key string) interface{} {
	if t.cache == nil {
		return nil
	}
	return t.cache[key]
}

type Node interface {
	TextRange

	ID() int
	SetID(id int)

	Flags() NodeFlags
	SetFlags(flags NodeFlags)
	AndFlags(flags NodeFlags)
	OrFlags(flags NodeFlags)

	Parent() Node
	SetParent(node Node)

	GetAnnotations() *NodeList
	GetModifiers() *NodeList
	AddDocuments(doc Document)
	GetDocuments() []Document
	aNode()
}

type Statement interface {
	Node
	aStatement()
}

type Expression interface {
	Node
	aExpression()
}

type Declaration interface {
	Node
	aDeclaration()
}

type Type interface {
	Node
	aType()
}

type Document interface {
	Node
	aDocument()
}

// Node
type node struct {
	flags  NodeFlags
	id     int // Unique ID (used to look up NodeLinks)
	parent Node

	Annotations *NodeList // Array of decorators (in document order)
	Modifiers   *NodeList

	Documents []Document

	textRange
}

func (n *node) ID() int { return n.id }
func (n *node) SetID(id int) { n.id = id }
func (n *node) Flags() NodeFlags { return n.flags }
func (n *node) SetFlags(flags NodeFlags) { n.flags = flags }
func (n *node) AndFlags(flags NodeFlags) { n.flags &= flags }
func (n *node) OrFlags(flags NodeFlags) { n.flags |= flags }
func (n *node) Parent() Node { return n.parent }
func (n *node) SetParent(node Node) { n.parent = node }
func (n *node) GetAnnotations() *NodeList { return n.Annotations }
func (n *node) GetModifiers() *NodeList { return n.Modifiers }
func (n *node) AddDocuments(doc Document) { n.Documents = append(n.Documents, doc) }
func (n *node) GetDocuments() []Document { return n.Documents }

func (n *node) aNode() {}

type (
	TokenNode struct {
		Token token.Token
		node
	}

	Modifier struct {
		Token token.Token
		node
	}

	Annotation struct {
		Type      Type
		Arguments *ExpressionList
		node
	}

	HeritageClause struct {
		Token token.Token
		Types *TypeList
		node
	}

	CaseClause struct {
		Token      *TokenNode
		Expression Expression // nil means default clause
		Statements *StatementList
		node
	}

	CaseBlock struct {
		Clauses *NodeList
		node
	}

	CatchClause struct {
		VariableDeclaration *VariableDeclaration
		Block               *BlockStatement
		node
	}

	// Language support
	SyntaxList struct {
		node
	}
)

type Signature interface {
	Declaration
	GetTypeParameters() *DeclarationList
	GetParameters() *DeclarationList
	GetType() Type
	GetThrows() *TypeList
}

type signature struct {
	TypeParameters *DeclarationList
	Parameters     *DeclarationList
	Type           Type
	Throws         *TypeList
}
func (s *signature) GetTypeParameters() *DeclarationList { return s.TypeParameters }
func (s *signature) GetParameters() *DeclarationList     { return s.Parameters }
func (s *signature) GetType() Type                       { return s.Type }
func (s *signature) GetThrows() *TypeList { return s.Throws }

type FunctionLike interface {
	Signature
	GetBody() Node
}
func (c *ConstructorDeclaration) GetBody() Node   { return c.Body }
func (m *MethodDeclaration) GetBody() Node        { return m.Body }
func (a *ArrowFunctionDeclaration) GetBody() Node { return a.Body }

type ObjectLike interface {
	Declaration
	GetTypeParameters() *DeclarationList
	GetHeritageClauses() *NodeList
	GetMembers() *DeclarationList
}

type objectLike struct {
	TypeParameters  *DeclarationList
	HeritageClauses *NodeList
	Members         *DeclarationList
	declaration
}
func (c *objectLike) GetTypeParameters() *DeclarationList { return c.TypeParameters }
func (c *objectLike) GetHeritageClauses() *NodeList       { return c.HeritageClauses }
func (c *objectLike) GetMembers() *DeclarationList        { return c.Members }

type VariableLike interface {
	Declaration
	GetType() Type
	GetInitializer() Expression
}

type variableLike struct {
	Type        Type
	Initializer Expression
	declaration
}
func (v *variableLike) GetType() Type              { return v.Type }
func (v *variableLike) GetInitializer() Expression { return v.Initializer }

// Decl
type declaration struct {
	node
}
func (d declaration) aDeclaration() {}

type (
	BadDeclaration struct {
		declaration
	}

	PackageDeclaration struct {
		Name Node
		declaration
	}

	ImportDeclaration struct {
		Path   Node
		Target Node
		declaration
	}

	VariableDeclaration struct {
		Name *Identifier
		variableLike
	}

	ParameterDeclaration struct {
		Name *Identifier
		Dots *TokenNode
		variableLike
	}

	DocumentTypeDeclaration struct {
		Dots *TokenNode
		Type Type
		declaration
	}

	TypeParameterDeclaration struct {
		Name       *Identifier
		Constraint Type
		declaration
	}

	ArrowFunctionDeclaration struct {
		signature
		Arrow *TokenNode
		Body  Node
		declaration
	}

	ConstructorDeclaration struct {
		signature
		Body *BlockStatement
		declaration
	}

	FieldDeclaration struct {
		Name *Identifier
		variableLike
	}

	MethodDeclaration struct {
		Name *Identifier
		signature
		Default *TokenNode
		Value   Expression
		Body    *BlockStatement
		declaration
	}

	InitializerDeclaration struct {
		Body    *BlockStatement
		declaration
	}

	ClassDeclaration struct {
		Name       *Identifier
		objectLike
	}

	BodyDeclaration struct {
		objectLike
	}

	EnumItemDeclaration struct {
		Name      *Identifier
		Arguments *ExpressionList
		Body      *BodyDeclaration
		declaration
	}

	EnumDeclaration struct {
		Name      *Identifier
		EnumItems *DeclarationList
		objectLike
	}

	InterfaceDeclaration struct {
		Name       *Identifier
		objectLike
	}

	AnnotationDeclaration struct {
		Name       *Identifier
		objectLike
	}
)

// Expr
type expression struct { node }
func (e expression) aExpression() { }

type(
	OmittedExpression struct {
		expression
	}

	Identifier struct {
		Value         string
		OriginalToken token.Token
		expression
	}

	ArrowExpression struct {
		Declaration *ArrowFunctionDeclaration
		expression
	}

	PrefixUnaryExpression struct {
		Operator *TokenNode
		Operand  Expression
		expression
	}

	PostfixUnaryExpression struct {
		Operand  Expression
		Operator *TokenNode
		expression
	}

	BinaryExpression struct {
		Left     Expression
		Operator *TokenNode
		Right    Expression
		expression
	}

	// Cond ? True : False
	ConditionalExpression struct {
		Condition   Expression
		QuestionTok *TokenNode
		WhenTrue    Expression
		ColonTok    *TokenNode
		WhenFalse   Expression
		expression
	}

	// This.Expression
	ThisExpression struct {
		Expression Expression
		expression
	}

	// Super.Expression
	SuperExpression struct {
		Expression Expression
		expression
	}

	QualifiedExpression struct {
		Type      Type
		expression
	}

	ConstructorExpression struct {
		Scope         Expression
		TypeName      Node
		TypeArguments *TypeList
		Arguments     *ExpressionList
		expression
	}

	NewObjectExpression struct {
		Type Expression
		Body *BodyDeclaration
		expression
	}

	NewArrayExpression struct {
		Type        Type
		Initializer *ArrayLiteralExpression
		expression
	}

	// Expression as Type
	AsExpression struct {
		Expression Expression
		Type       Type
		expression
	}

	// {0, 1, 2}
	ArrayLiteralExpression struct {
		Elements  *ExpressionList
		expression
	}

	// (Expression)
	ParenthesizedExpression struct {
		Expression Expression
		expression
	}

	// Expression[ArgumentExpression]
	ElementAccessExpression struct {
		Expression         Expression
		ArgumentExpression Expression
		expression
	}

	// "str" 10 10L
	LiteralExpression struct {
		Token token.Token
		Value string
		expression
	}

	// int.class
	ClassExpression struct {
		Type Expression
		expression
	}

	QualifiedName struct {
		Left  Expression
		Right *Identifier
		expression
	}

	// String#replace
	InstanceAccessExpression struct {
		Left  Expression
		Right *Identifier
		expression
	}

	// Expression.Name
	SelectorExpression struct {
		Expression Expression
		Name       *Identifier
		expression
	}

	// Expression<TypeArguments>(Arguments)
	CallExpression struct {
		Expression    Expression
		TypeArguments *TypeList
		Arguments     *ExpressionList
		expression
	}

	// Expression(Arguments)
	DocumentCallExpression struct {
		Expression    Expression
		Arguments     *DeclarationList
		expression
	}
)

// Stmt
type statement struct { node }
func (s statement) aStatement() { }

type (
	EmptyStatement struct {
		statement
	}

	DeclarationStatement struct {
		Declaration Declaration
		statement
	}

	ExpressionStatement struct {
		Expression Expression
		statement
	}

	BlockStatement struct {
		Statements *StatementList
		statement
	}

	TryStatement struct {
		TryBlock     *BlockStatement
		CatchClauses *NodeList
		FinallyBlock *BlockStatement
		statement
	}

	IfStatement struct {
		Condition     Expression
		ThenStatement *BlockStatement
		ElseStatement Statement // either nil, *IfStmt, or *BlockStmt
		statement
	}

	DoStatement struct {
		Body      *BlockStatement
		Condition Expression
		statement
	}

	WhileStatement struct {
		Condition Expression
		Body      *BlockStatement
		statement
	}

	ForStatement struct {
		Initializer Node
		Condition   Expression
		Incrementor Expression
		Body        *BlockStatement
		statement
	}

	ForOfStatement struct {
		VariableDeclaration *VariableDeclaration
		Expression          Expression
		Body                *BlockStatement
		statement
	}

	BranchStatement struct {
		Token token.Token // Break, Continue, Fallthrough, or Goto
		Label *Identifier
		statement
	}

	ThrowStatement struct {
		Expression Expression
		statement
	}

	ReturnStatement struct {
		Expression Expression
		statement
	}

	SwitchStatement struct {
		Expression         Expression
		Body               *CaseBlock
		PossiblyExhaustive bool
		statement
	}

	LabeledStatement struct {
		Label     *Identifier
		Statement Statement
		statement
	}
)

// Type
type tpe struct { node }
func (t tpe) aType() { }

type (
	BoundsType struct {
		Bound *TokenNode
		Type  Type
		tpe
	}

	IntrinsicType struct {
		Token token.Token
		tpe
	}

	TypeReference struct {
		Name          Node
		TypeArguments *TypeList
		tpe
	}

	ArrayType struct {
		Element   Type
		Dimension Expression
		tpe
	}

	UnionType struct {
		Types *TypeList
		tpe
	}

	IntersectionType struct {
		Types *TypeList
		tpe
	}
)

const AuthorTagStr = "作者"
const ParameterTagStr = "参数"
const ReturnTagStr = "返回"
const SeeTagStr = "另见"
const DeprecatedTagStr = "弃用"
const ExceptionTagStr = "异常"
const SinceTagStr = "初版"
const VersionTagStr = "版本"
const AliasTagStr = "别名"

// Document
type document struct {
	node
}

func (d *document) aDocument() {}
type (
	DocRoot struct {
		Children *DocumentList
		document
	}

	Number struct {
		Value string
		document
	}

	Version struct {
		Left	Node
		Right	*Number
		document
	}

	PlainText struct {
		Value string
		document
	}

	Header struct {
		Token token.Token
		Text *PlainText
		document
	}

	Bold struct {
		Text *PlainText
		document
	}

	Italic struct {
		Text *PlainText
		document
	}

	BoldItalic struct {
		Text *PlainText
		document
	}

	Paragraph struct {
		Content *DocumentList
		document
	}

	CodeText struct {
		Content *PlainText
		document
	}

	CodeBlock struct {
		Language *PlainText
		Content  *DocumentList
		document
	}

	CodeLink struct {
		Name     *PlainText
		Expression Expression
		document
	}

	Link struct {
		Name    *PlainText
		Address *PlainText
		document
	}

	LineBreak struct {
		document
	}

	OrderedList struct {
		Level   int
		Content *DocumentList
		document
	}

	UnorderedList struct {
		Level   int
		Content *DocumentList
		document
	}

	ListItem struct {
		Content *Paragraph
		document
	}

	AuthorTag struct {
		Content *Paragraph
		document
	}

	AliasTag struct {
		Name *Identifier
		Content *Paragraph
		document
	}

	ParameterTag struct {
		IsTypeParameter bool
		Name            *Identifier
		Content         *Paragraph
		document
	}

	ReturnTag struct {
		Content *Paragraph
		document
	}

	ExceptionTag struct {
		Type Type
		Content *Paragraph
		document
	}

	SeeTag struct {
		Expression Expression
		Content    *Paragraph
		document
	}

	SinceTag struct {
		Version Node // Version or Number
		Content *Paragraph
		document
	}

	VersionTag struct {
		Version Node // Version or Number
		Content *Paragraph
		document
	}

	DeprecatedTag struct {
		Content *Paragraph
		document
	}
)

func (a *AliasTag) aDeclaration() {}

type ScriptKind int
const MI ScriptKind = 0

type ScriptTarget int
const Java8 ScriptTarget = 0

type SourceFile struct {
	PackageDeclaration *PackageDeclaration
	Imports            *DeclarationList
	Declarations       *DeclarationList

	Uri  xpath.URI
	Text []byte

	EndOfFileToken *TokenNode

	// ReferencedPackages   []*FileReference
	IsDeclarationFile bool

	HasNoDefaultLib         bool
	NodeCount               int
	IdentifierCount         int
	SymbolCount             int
	IsOpen                  bool
	Version                 string
	LanguageVersion         ScriptTarget
	ScriptKind              ScriptKind
	Identifiers             map[string]string
	LineStarts              []int
	declaration
}

func (s *SourceFile) GetLineStarts() []int{
	if s.LineStarts == nil {
		s.LineStarts = span.GetLineStarts(s.Text)
	}
	return s.LineStarts
}

type ProjectFile struct {
	Uri  xpath.URI
	Text []byte

	Version    string   `json:"version"`
	MainClass  string   `json:"main_class"`
	Dependents []string `json:"dependents"`

	SourcePath string
	OutputPath string

	Packages map[string]*Package

	ResolvedPackageTree map[*PackageTree]*Package
	ResolvedDependents  []*ProjectFile
	IsDefault           bool

	declaration
}

type PackageTree struct {
	Name     string
	Parent   *PackageTree
	Packages []*Package
	SubTrees map[string]*PackageTree
}

type Status int

const (
	None    Status = 0
	PreLoad Status = 1 << iota
	Loaded
	Bind
)

type Package struct {
	Status       Status
	PackageName  string
	Version      string
	DiskPath     xpath.URI
	Tree         *PackageTree
	ProjectFile  *ProjectFile
	Files        map[string]*SourceFile
	ResolvedPath *string
	declaration
}

type NodeFlags int
const (
	NFNone NodeFlags = 1 << iota
	NestedNamespace
	Synthesized
	Namespace
	ExportContext
	ContainsThis
	HasImplicitReturn
	HasExplicitReturn
	GlobalAugmentation
	HasAsyncFunctions
	ThisNodeHasError
	ThisNodeOrAnySubNodesHasError
	HasAggregatedChildData

	ReachabilityCheckFlags   = HasImplicitReturn | HasExplicitReturn
	ReachabilityAndEmitFlags = ReachabilityCheckFlags | HasAsyncFunctions

	// Parsing context flags
	ContextFlags = NFNone

	// Exclude these flags when parsing a Type
	TypeExcludesFlags = NFNone
)

type ModifierFlags int

const (
	MFNone      ModifierFlags = 1 << iota

	// AccessibilityModifier
	// Public > Protected > Friend > Private

	MFPublic
	MFProtected
	MFFriend
	MFPrivate


	MFStatic                  // Property/Method
	MFAbstract                // Class/Method/ConstructSi
	MFDefault				  // Interface default implement method
	MFFinal					  // Class/Method/Field/Variable
	MFTransient
	MFVolatile
	MFNative
	MFSynchronized

	MFAccessibilityModifier          = MFPublic|MFProtected|MFFriend|MFPrivate
	MFNonPublicAccessibilityModifier = MFProtected|MFFriend|MFPrivate
)

const ConstructorName = "[构造]"

type CommentRange struct {
	textRange
	HasTrailingNewLine bool
}
